﻿using System.Runtime.CompilerServices;
using Dyao.Book.Contract;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Dyao.Book.Application
{
    public static partial class TaskCenterManagerExtension
    {
        private static Type _updatingCheckerType = typeof(ITaskCenterUpdatingChecker);

        #region // 1个泛型参数 分布式任务

        /// <summary>
        /// 开始执行分布式主任务
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func"></param>
        /// <param name="executingInfo"></param>
        /// <param name="taskCenterModel"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static Task BeginDistributedMainTaskAsync<TResult>(this IServiceProvider serviceProvider,
            Func<TaskCenterDistributedContenxt<TResult>, Task> func,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
        {
            return RunDistributedTaskAsync(serviceProvider, func, null, true, taskCenterModel, cancellationToken);
        }

        /// <summary>
        /// 开始执行分布式子任务
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func"></param>
        /// <param name="taskCenterModel"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static Task BeginDistributedSubTaskAsync<TResult>(this IServiceProvider serviceProvider,
            Func<TaskCenterDistributedContenxt<TResult>, Task> func,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
        {
            return RunDistributedTaskAsync(serviceProvider, func, null, false, taskCenterModel, cancellationToken);
        }

        /// <summary>
        /// 执行分布式任务
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func"></param>
        /// <param name="isMainTask">是否主任务</param>
        /// <param name="executingInfo"></param>
        /// <param name="taskCenterModel"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task RunDistributedTaskAsync<TResult>(this IServiceProvider serviceProvider,
            Func<TaskCenterDistributedContenxt<TResult>, Task> func,
            Func<TaskCenterDistributedCheckContext<TResult>, bool> completeChecker,
            bool isMainTask,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
        {
            var manager = serviceProvider.CreateTaskCenterManager<TResult>(taskCenterModel, true, cancellationToken);

            try
            {
                var context = new TaskCenterDistributedContenxt<TResult>(taskCenterModel, serviceProvider);

                if (completeChecker == null && IsUpdatingCheckerImpl<TResult>())
                {
                    completeChecker = (checkerContext) => (checkerContext.Result is ITaskCenterUpdatingChecker checker) ? checker.IsCompleted() : false;
                }

                context.CompleteChecker = completeChecker;
                context.SetOnUpdate(action =>
                {
                    return manager.UpdateResultAsync(model =>
                    {
                        var item = LoadItem(model, new TaskCenterUpdatingResultModel<TResult> { Result = model.ExcuteResult });

                        action(item);

                        if (context.CompleteChecker != null)
                        {
                            var checkContext = new TaskCenterDistributedCheckContext<TResult>(item.Result);
                            if (context.CompleteChecker(checkContext))
                            {
                                item.Progress = 100;
                            }
                        }

                        StoreItem(model, item);
                        model.ExcuteResult = item.Result;

                    }, isAutoComplete: true);
                });
                context.SetLoadRequestParams(manager.GetRequestParamsAsync);

                if (isMainTask)
                {
                    await manager.StartAsync();
                }

                await func(context);
            }
            catch (Exception ex)
            {
                var taskId = taskCenterModel.TaskCenterRecordId;
                var logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("DistributedTask");
                var dict = new Dictionary<string, object>()
                {
                    { "recordId", taskId},
                    { "currentRetryCount", taskCenterModel.CurrentRetryCount},
                    { "subTask", isMainTask?"Main":"Sub" }
                };

                using (var scope = logger.BeginScope(dict))
                {
                    logger.LogError(ex, $"任务id:{taskId},执行失败:{ex.Message}");
                }

                try
                {

                    await manager.FailAsync(ex);
                }
                catch (Exception fex)
                {
                    using (var fscope = logger.BeginScope(dict))
                    {
                        logger.LogError(fex, $"任务id:{taskId},执行更新失败信息时失败:{fex.Message}");
                    }
                }
            }
        }

        #endregion

        #region //2个泛型参数 分布式任务

        /// <summary>
        /// 开始执行分布式主任务
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <typeparam name="TExecutingInfo"></typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func"></param>
        /// <param name="executingInfo"></param>
        /// <param name="taskCenterModel"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static Task BeginDistributedMainTaskAsync<TResult, TExecutingInfo>(this IServiceProvider serviceProvider,
            Func<TaskCenterDistributedContenxt<TResult, TExecutingInfo>, Task> func,
            TExecutingInfo executingInfo,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
            where TExecutingInfo : class, new()
        {
            return RunDistributedTaskAsync(serviceProvider, func, null, true, executingInfo, taskCenterModel, cancellationToken);
        }

        /// <summary>
        /// 开始执行分布式子任务
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <typeparam name="TExecutingInfo"></typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func"></param>
        /// <param name="taskCenterModel"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static Task BeginDistributedSubTaskAsync<TResult, TExecutingInfo>(this IServiceProvider serviceProvider,
            Func<TaskCenterDistributedContenxt<TResult, TExecutingInfo>, Task> func,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
            where TExecutingInfo : class, new()
        {
            return RunDistributedTaskAsync(serviceProvider, func, null, false, null, taskCenterModel, cancellationToken);
        }

        /// <summary>
        /// 执行分布式任务
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <typeparam name="TExecutingInfo"></typeparam>
        /// <param name="serviceProvider"></param>
        /// <param name="func"></param>
        /// <param name="isMainTask">是否主任务</param>
        /// <param name="executingInfo"></param>
        /// <param name="taskCenterModel"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public static async Task RunDistributedTaskAsync<TResult, TExecutingInfo>(this IServiceProvider serviceProvider,
            Func<TaskCenterDistributedContenxt<TResult, TExecutingInfo>, Task> func,
            Func<TaskCenterDistributedCheckContext<TResult, TExecutingInfo>, bool> completeChecker,
            bool isMainTask,
            TExecutingInfo executingInfo,
            TaskCenterDto taskCenterModel,
            CancellationToken cancellationToken = default)
            where TExecutingInfo : class, new()
        {
            if (taskCenterModel is null)
            {
                throw new ArgumentNullException(nameof(taskCenterModel));
            }

            var manager = serviceProvider.CreateTaskCenterManager<TResult, TExecutingInfo>(taskCenterModel, true, cancellationToken);

            try
            {
                var context = new TaskCenterDistributedContenxt<TResult, TExecutingInfo>(taskCenterModel, serviceProvider);

                if (completeChecker == null &&
                    ((isMainTask && executingInfo is ITaskCenterUpdatingChecker)) || IsUpdatingCheckerImpl<TExecutingInfo>())
                {
                    completeChecker = (checkerContext) => (checkerContext.Info is ITaskCenterUpdatingChecker checker) ? checker.IsCompleted() : false;
                }

                context.CompleteChecker = completeChecker;

                context.SetOnUpdate(action =>
                {
                    return manager.UpdateAsync(model =>
                    {
                        var item = LoadItem(model, new TaskCenterUpdatingInfoModel<TResult, TExecutingInfo>(model.ExcutingInfo) { Result = model.ExcuteResult });

                        action(item);

                        if (context.CompleteChecker != null)
                        {
                            var checkContext = new TaskCenterDistributedCheckContext<TResult, TExecutingInfo>(item.Info, item.Result);
                            if (context.CompleteChecker(checkContext))
                            {
                                item.Progress = 100;
                            }
                        }

                        StoreItem(model, item);
                        model.ExcuteResult = item.Result;

                    }, isAutoComplete: true);
                });
                context.SetLoadRequestParams(manager.GetRequestParamsAsync);

                //开始任务
                if (isMainTask)
                {
                    executingInfo ??= new TExecutingInfo();
                    await manager.StartAsync(executingInfo);
                }

                //业务逻辑执行
                await func(context);
            }
            catch (Exception ex)
            {
                var taskId = taskCenterModel.TaskCenterRecordId;
                var logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger("DistributedTask");
                var dict = new Dictionary<string, object>()
                {
                    { "recordId", taskId},
                    { "currentRetryCount", taskCenterModel.CurrentRetryCount},
                    { "subTask", isMainTask?"Main":"Sub" }
                };

                using (var scope = logger.BeginScope(dict))
                {
                    logger.LogError(ex, $"任务id:{taskId},执行失败:{ex.Message}");
                }

                try
                {

                    await manager.FailAsync(ex);
                }
                catch (Exception fex)
                {
                    using (var fscope = logger.BeginScope(dict))
                    {
                        logger.LogError(fex, $"任务id:{taskId},执行更新失败信息时失败:{fex.Message}");
                    }
                }
            }
        }

        #endregion

        #region private methods

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static bool IsUpdatingCheckerImpl<T>()
        {
            return _updatingCheckerType.IsAssignableFrom(typeof(T));
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static T LoadItem<T>(TaskCenterRecordProgressModel model, T item)
            where T : TaskCenterUpdatingProgressModel
        {
            item.ResultMessage = model.ResultMessage;
            item.Progress = model.ExcutingProgress;

            return item;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static void StoreItem(TaskCenterRecordProgressModel model, TaskCenterUpdatingProgressModel item)
        {
            model.ResultMessage = item.ResultMessage;
            model.ExcutingProgress = item.Progress;
        }

        #endregion
    }
}
